/***************************************************************
 * @System            : 生鲜系统
 * @Content           : 继承 TreeListLookUpEdit，增加相关通用方法或事件
 * @Author            : geping  2018-10-05
 * @Modify            : 
 * @Version           : v1.0
 * @Copyright         : 宁波康铭泰克信息科技有限公司
 ***************************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DevExpress.Accessibility;
using DevExpress.Data.Filtering;
using DevExpress.Utils.Editors;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.Controls;
using DevExpress.XtraEditors.Drawing;
using DevExpress.XtraEditors.Registrator;
using DevExpress.XtraEditors.Repository;
using DevExpress.XtraGrid;
using DevExpress.XtraTreeList;
using DevExpress.XtraTreeList.Columns;
using DevExpress.XtraTreeList.Nodes;

namespace Comteck.Winforms.Controls {
  [ToolboxItem(true)]
  public class MyTreeListLookUpEdit : TreeListLookUpEdit {
    private RepositoryItemTreeListLookUpEdit fProperties;
    private TreeList fPropertiesTreeList;

    static MyTreeListLookUpEdit() {
      RepositoryItemMyTreeListLookUpEdit.Register();
    }

    /// <summary>
    /// 
    /// </summary>
    public MyTreeListLookUpEdit() : base() {
      Initialize();
    }

    /// <summary>
    /// 初始化界面
    /// </summary>
    private void Initialize() {
  /*    this.Tag = false; */  // 设置全选标记

      //// 鼠标移入文本编辑框触发事件
      //this.Enter += (sender, e) => {
      //  // 设置全选标记
      //  this.Tag = true;
      //  this.SelectAll();
      //};
      // 获取输入焦点时自动全选
      //this.MouseUp += (sender, e) => {
      //  // 如果鼠标左键操作并且标记存在，则执行全选
      //  if (e.Button == MouseButtons.Left && (bool)this.Tag) {
      //    this.SelectAll();
      //  }

      //  // 取消全选标记
      //  this.Tag = false;
      //};
      // 双击输入框时自动全选
      this.DoubleClick += (sender, e) => {
        this.SelectAll();
      };
      // 回车事件
      KeyDown += this.MyTreeListLookUpEdit_KeyDown;

      //EditValueChanging += this.MyTreeListLookUpEdit_EditValueChanging;
    }

    #region fields

    /// <summary>
    /// 
    /// </summary>
    public override string EditorTypeName => RepositoryItemMyTreeListLookUpEdit.EditorName;

    /// <summary>
    /// 
    /// </summary>
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public new RepositoryItemMyTreeListLookUpEdit Properties => base.Properties as RepositoryItemMyTreeListLookUpEdit;

    //
    // 摘要:
    //     Gets or sets whether focus is moved to the next control (according to the tab
    //     order) when an end-user presses ENTER.
    [DefaultValue(true)]
    [DXCategory("Behavior")]
    public override bool EnterMoveNextControl { get; set; } = true;

    #endregion

    #region events
       
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void MyTreeListLookUpEdit_KeyDown(object sender, KeyEventArgs e) {

      if (e.KeyCode == Keys.Delete) {
        var control = sender as BaseEdit;
        // 非只读才能置空
        if (!control.Properties.ReadOnly) {
          control.EditValue = null;
        }
        e.Handled = true;
      } else if (e.KeyCode == Keys.Back) {
        if (this.IsPopupOpen == false) {
          this.ShowPopup();

          e.Handled = true;
        }

      } else if (e.KeyCode == Keys.Down) {
        if (this.IsPopupOpen == false) {
          this.ShowPopup();

          // 默认选中第一项
          if (this.EditValue == null && this.Properties.TreeList.Nodes.Count > 0) {
            this.Properties.TreeList.FocusedNode = this.Properties.TreeList.Nodes[0];
            this.Properties.TreeList.SelectNode(this.Properties.TreeList.Nodes[0]);
          }

          e.Handled = true;
        }
      }
    }

    #region 公用方法 add by geping@2018-10-05

    /// <summary>
    /// 根据指定值设置EditValue
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="K">K类型必须同fieldName对应的字段同类型</typeparam>
    /// <param name="value">赋值数据</param>
    /// <param name="fieldName">匹配的字段</param>
    /// <returns></returns>
    public void SetEditValue<T, K>(K value, string fieldName) {
      if (value == null) {
        this.EditValue = null;
        return;
      }

      var source = this.Properties.DataSource as List<T>;
      var nodeList = this.Properties.TreeList.Nodes;

      this.SetEditValue<T, K>(source, nodeList, value, fieldName);
    }
    /// <summary>
    /// 根据指定值设置EditValue
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="K"></typeparam>
    /// <param name="source">数据源</param>
    /// <param name="nodeList">所有节点</param>
    /// <param name="value">赋值数据</param>
    /// <param name="fieldName">匹配字段</param>
    private bool SetEditValue<T, K>(List<T> source, TreeListNodes nodeList, K value, string fieldName) {
      for (var i = 0; i < nodeList.Count; i++) {
        var entity = source[nodeList[i].Id];

        foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(typeof(T))) {
          if (!pd.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase)) continue;

          // 判断传入的类型与指定字段的类型是否一致
          if (typeof(K) != pd.PropertyType) continue;

          // 如果匹配，则赋值
          if (value.Equals((K)(pd.GetValue(entity) ?? default(K)))) {
            foreach (PropertyDescriptor p in TypeDescriptor.GetProperties(typeof(T))) {
              if (!p.Name.Equals(this.Properties.ValueMember)) continue;

              this.EditValue = p.GetValue(entity) ?? DBNull.Value;
              this.Properties.TreeList.FocusedNode = nodeList[i];
              return true;
            }
          }
          // 如果字段匹配上但是值不匹配，则跳过继续查找下一个节点
          break;
        }

        // 遍历子节点
        if (this.SetEditValue<T, K>(source, nodeList[i].Nodes, value, fieldName)) {
          return true;
        }
      }

      // 没有匹配上数据
      this.EditValue = null;
      return false;
    }
    /// <summary>
    /// 返回选中项列表（只返回叶子节点）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public IEnumerable<T> GetCheckedTreeListNodes<T>() {
      return this.Properties.GetCheckedTreeListNodes<T>();
    }
    /// <summary>
    /// 返回选中项列表（返回所有节点）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public IEnumerable<T> GetCheckedTreeListNodesFull<T>() {
      return this.Properties.GetCheckedTreeListNodesFull<T>();
    }
    /// <summary>
    /// 返回选中项列表（包括未确定的状态）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public IEnumerable<T> GetCheckedTreeListNodesIndeterminate<T>() {
      return this.Properties.GetCheckedTreeListNodesIndeterminate<T>();
    }
    /// <summary>
    /// 返回待显示的字符串提示
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public string GetToolTip<T>() {
      return this.Properties.GetToolTip<T>();
    }
    /// <summary>
    /// 设置已勾选状态
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="K"></typeparam>
    /// <param name="data">待勾选的数据集合，K类型必须同fieldName对应的字段同类型</param>
    /// <param name="fieldName">匹配的字段</param>
    public void SetCheckedTreeListNodes<T, K>(IEnumerable<K> data, string fieldName) {
      this.Properties.SetCheckedTreeListNodes<T, K>(data, fieldName);
    }

    #endregion

    //protected override void OnEditValueChanged() {
    //  base.OnEditValueChanged();

    //  //this.Properties.SetCriteriaOperator(null);

    //  //var criteriaString = string.Empty;
    //  //foreach (DevExpress.XtraTreeList.Columns.TreeListColumn col in this.Properties.TreeList.Columns) {
    //  //  criteriaString += "Contains([" + col.FieldName + "], '" + this.Text + "')";
    //  //this.Properties.SetCriteriaOperator(criteriaString);
    //  //}
    //}

    //private void MyTreeListLookUpEdit_EditValueChanging(object sender, ChangingEventArgs e) {
    //  this.BeginInvoke(new System.Windows.Forms.MethodInvoker(delegate {
    //    FilterLookup(sender, new string[] { "Code", "Name", "PyCode" });
    //  }));
    //}
    ///// <summary>
    ///// 
    ///// </summary>
    ///// <param name="sender"></param>
    ///// <param name="arrFilterField"></param>
    //private void FilterLookup(object sender, string[] arrFilterField) {
    //  TreeListLookUpEdit edit = sender as TreeListLookUpEdit;
    //  TreeList treeList = edit.Properties.TreeList as TreeList;
    //  System.Reflection.FieldInfo fi = treeList.GetType().GetField("extraFilter", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    //  DevExpress.Data.Filtering.CriteriaOperator[] arrCriteriaOperator = new DevExpress.Data.Filtering.CriteriaOperator[arrFilterField.Length];
    //  for (int i = 0; i < arrFilterField.Length; i++) {
    //    arrCriteriaOperator[i] = new DevExpress.Data.Filtering.BinaryOperator(arrFilterField[i], "%" + edit.AutoSearchText + "%", DevExpress.Data.Filtering.BinaryOperatorType.Like);
    //  }
    //  string filterCondition = new GroupOperator(DevExpress.Data.Filtering.GroupOperatorType.Or, arrCriteriaOperator).ToString();
    //  fi.SetValue(treeList, filterCondition);
    //  System.Reflection.MethodInfo mi = treeList.GetType().GetMethod("ApplyColumnsFilterEx", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    //  mi.Invoke(treeList, null);
    //}

    #endregion

    private void InitializeComponent() {
      this.fProperties = new DevExpress.XtraEditors.Repository.RepositoryItemTreeListLookUpEdit();
      this.fPropertiesTreeList = new DevExpress.XtraTreeList.TreeList();
      ((System.ComponentModel.ISupportInitialize)(this.fProperties)).BeginInit();
      ((System.ComponentModel.ISupportInitialize)(this.fPropertiesTreeList)).BeginInit();
      this.SuspendLayout();
      // 
      // fProperties
      // 
      this.fProperties.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] {
            new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Combo)});
      this.fProperties.Name = "fProperties";
      this.fProperties.TreeList = this.fPropertiesTreeList;
      // 
      // fPropertiesTreeList
      // 
      this.fPropertiesTreeList.Location = new System.Drawing.Point(0, 0);
      this.fPropertiesTreeList.Name = "fPropertiesTreeList";
      this.fPropertiesTreeList.OptionsView.ShowIndentAsRowStyle = true;
      this.fPropertiesTreeList.Size = new System.Drawing.Size(400, 200);
      this.fPropertiesTreeList.TabIndex = 0;
      ((System.ComponentModel.ISupportInitialize)(this.fProperties)).EndInit();
      ((System.ComponentModel.ISupportInitialize)(this.fPropertiesTreeList)).EndInit();
      this.ResumeLayout(false);

    }
  }

  /// <summary>
  /// 
  /// </summary>
  [DXCategory("Properties")]
  [UserRepositoryItem("Register")]
  [ToolboxItem(false)]
  [Designer("DevExpress.XtraTreeList.Design.TreeListLookUpEditRepositoryItemDesigner, " + AssemblyInfo.SRAssemblyTreeListDesign)]
  public class RepositoryItemMyTreeListLookUpEdit : RepositoryItemTreeListLookUpEdit {
    public delegate void OnCustomMethodDelegate();
    /// <summary>
    /// 自定义事件  add by geping@2018-10-05
    /// </summary>
    public OnCustomMethodDelegate OnCustomMethodEventHandler;

    /// <summary>
    /// 注册编辑器
    /// </summary>
    static RepositoryItemMyTreeListLookUpEdit() {
      Register();
    }

    /// <summary>
    /// 创建自定义的编辑器
    /// </summary>
    public RepositoryItemMyTreeListLookUpEdit() {
      // 是否用相应Node的Appearance设置来生成Tree的缩进。默认为False
      this.TreeList.OptionsView.ShowIndentAsRowStyle = true;
    }

    /// <summary>
    /// 编辑器的名称
    /// </summary>
    internal new const string EditorName = nameof(MyTreeListLookUpEdit);

    /// <summary>
    /// 
    /// </summary>
    public override string EditorTypeName => EditorName;

    ///// <summary>
    ///// 
    ///// </summary>
    ///// <returns></returns>
    //protected override TreeList CreateTreeList() {
    //  return base.CreateTreeList();
    //}

    ///// <summary>
    ///// 
    ///// </summary>
    //[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    //[DXCategory("TreeList")]
    //[Editor("DevExpress.XtraTreeList.Design.TreeListLookUpEditor, DevExpress.XtraTreeList.v17.2.Design", typeof(UITypeEditor))]
    //[TypeConverter(typeof(ExpandableObjectConverter))]
    //public new TreeList TreeList => base.TreeList as TreeList;

    /// <summary>
    /// 下拉框样式
    /// </summary>
    [DefaultValue(TextEditStyles.Standard)]
    [DXCategory("Behavior")]
    public override TextEditStyles TextEditStyle { get; set; } = TextEditStyles.Standard;

    //
    // 摘要:
    //     Gets or sets whether value scrolling via the Mouse Wheel is supported by the
    //     editor.
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public override bool AllowMouseWheel { get; set; } = false;

    /// <summary>
    /// 是否默认展开下拉框
    /// </summary>
    [DefaultValue(true)]
    [DXCategory("Behavior")]
    public new virtual bool AutoExpandAllNodes { get; set; } = false;
    
    /// <summary>
    /// 
    /// </summary>
    public static void Register() {
      EditorRegistrationInfo.Default.Editors.Add(
        new EditorClassInfo(
          EditorName,
          typeof(MyTreeListLookUpEdit),
          typeof(RepositoryItemMyTreeListLookUpEdit),
          typeof(DevExpress.XtraEditors.ViewInfo.TreeListLookUpEditBaseViewInfo),
          new ButtonEditPainter(),
          true,
          EditImageIndexes.LookUpEdit,
          typeof(PopupEditAccessible)
          )
        );
    }

    /// <summary>
    /// TODO：
    /// 设置复选框联动，用于通过代码配置复选框显示
    /// 注意：不能与代码设计同时设置，有冲突
    /// add by geping@2019-01-12
    /// </summary>
    public void ShowCheckBoxes() {
      // 显示复选框
      this.TreeList.OptionsView.ShowCheckBoxes = true;
      // 隐藏文本框
      this.TextEditStyle = TextEditStyles.HideTextEditor;
      // 节点选中后事件
      this.TreeList.AfterCheckNode += (sender, e) => {
        // 设置父节点状态
        SetCheckedChildNodes(e.Node, e.Node.CheckState);
        // 设置子节点状态
        SetCheckedParentNodes(e.Node, e.Node.CheckState);
        // 加载自定义事件
        OnCustomMethodEventHandler?.Invoke();
      };
    }

    #region 递归选择 add by geping@2018-10-05

    /// <summary>
    /// 控件加载完毕后
    /// </summary>
    public override void EndInit() {
      base.EndInit();

      this.TreeList.OptionsFilter.FilterMode = FilterMode.Smart;
      this.TreeList.OptionsBehavior.EnableFiltering = true;

      // 如果允许多选的情况，注意对控件属性的判断必须放到此处，否则取到的值是设计器设计之前的初始值
      if (this.TreeList.OptionsView.ShowCheckBoxes) {
        // 隐藏文本框
        this.TextEditStyle = TextEditStyles.HideTextEditor;
        // 节点选中后事件
        this.TreeList.AfterCheckNode += (sender, e) => {
          // 设置父节点状态
          SetCheckedChildNodes(e.Node, e.Node.CheckState);
          // 设置子节点状态
          SetCheckedParentNodes(e.Node, e.Node.CheckState);
          // 加载自定义事件
          OnCustomMethodEventHandler?.Invoke();
        };
      }

      //this.TreeList.FilterNode += (sender, e) => {
      //  List<TreeListColumn> filteredColumns = e.Node.TreeList.Columns.Cast<TreeListColumn>().Where(c => c.FilterInfo.AutoFilterRowValue != null).ToList();
      //  if (filteredColumns.Count == 0) return;
      //  e.Handled = true;
      //  e.Node.Visible = filteredColumns.Any(c => IsNodeMatchFilter(e.Node, c));
      //};

      //this.TreeList.NodesIterator.DoOperation(x => {
      //  int val = Convert.ToInt32(x.GetValue(this.ValueMember));
      //});

      //this.TreeList.FilterNode += (sender, e) => {
      //  string text = e.Node.TreeList.FindFilterText.ToLower();

      //  for (int i = 0; i < this.TreeList.Columns.Count; i++) {
      //    e.Node.Visible = this.IsMatchFilter(e.Node, text, i) >= 0;
      //    e.Handled = true;
      //  }
      //};

      //this.TreeList.CustomDrawNodeCell += (sender, e) => {
      //  TreeList treeList = sender as TreeList;
      //  string text = e.Node.TreeList.FindFilterText.ToLower();

      //  if (treeList != null && treeList.OptionsFind.HighlightFindResults && !string.IsNullOrWhiteSpace(text)) {
      //    for (int i = 0; i < this.TreeList.Columns.Count; i++) {
      //      int idx = IsMatchFilter(e.Node, text, i);
      //      if (idx >= 0) {
      //        e.Appearance.FillRectangle(e.Cache, e.Bounds);
      //        e.Cache.Paint.DrawMultiColorString(
      //          e.Cache,
      //          e.Bounds,
      //          e.Node.GetDisplayText(i),
      //          e.Node.GetDisplayText(i).Substring(idx, treeList.FindFilterText.Length),
      //          e.Appearance,
      //          System.Drawing.Color.Black,
      //          System.Drawing.Color.FromArgb(255, 255, 210, 0),
      //          false,
      //          idx);
      //        e.Handled = true;
      //      }
      //    }
      //  }
      //};
    }

    /// <summary>
    /// 选择父节点时触发子节点状态变更
    /// </summary>
    /// <param name="node"></param>
    /// <param name="check"></param>
    /// <param name="action"></param>
    public void SetCheckedChildNodes(TreeListNode node, CheckState check, Action<TreeListNode, CheckState> action = null) {
      // 遍历子节点
      for (int i = 0; i < node.Nodes.Count; i++) {
        node.Nodes[i].CheckState = check;
        // 继续递归子节点
        SetCheckedChildNodes(node.Nodes[i], check);
        action?.Invoke(node.Nodes[i], check);
      }
    }

    /// <summary>
    /// 选择子节点时触发父节点状态变更
    /// </summary>
    /// <param name="node"></param>
    /// <param name="check"></param>
    /// <param name="action"></param>
    public void SetCheckedParentNodes(TreeListNode node, CheckState check, Action<TreeListNode, CheckState> action = null) {
      // 如果存在父节点，则判断兄弟节点的状态是否一致
      if (node.ParentNode != null) {
        bool b = false;
        CheckState state;

        // 遍历兄弟节点
        for (int i = 0; i < node.ParentNode.Nodes.Count; i++) {
          // 兄弟节点的状态
          state = (CheckState)node.ParentNode.Nodes[i].CheckState;
          // 存在不同状态的兄弟节点，则直接跳出循环
          if (!check.Equals(state)) {
            b = !b;
            break;
          }
        }

        node.ParentNode.CheckState = b ? CheckState.Indeterminate : check;
        // 继续向上递归父节点
        SetCheckedParentNodes(node.ParentNode, check);
        action?.Invoke(node.ParentNode, node.ParentNode.CheckState);
      }
    }

    /// <summary>
    /// 返回选中项列表（只返回叶子节点）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public IEnumerable<T> GetCheckedTreeListNodes<T>() {
      var list = new List<T>();

      switch (this.TreeList.OptionsView.ShowCheckBoxes) {
        case true: // 复选模式
          foreach (TreeListNode node in this.TreeList.Nodes) {
            // 递归方法
            GetCheckedTreeListNodes<T>(node, list);
          }
          break;
        case false: // 单选模式
          var singleNode = this.TreeList.GetDataRecordByNode(this.TreeList.FocusedNode);
          if (this.TreeList.GetDataRecordByNode(this.TreeList.FocusedNode) != null)
            list.Add((T)singleNode);
          break;
      }

      return list;
    }

    /// <summary>
    /// 返回选中项列表（返回所有节点）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public IEnumerable<T> GetCheckedTreeListNodesFull<T>() {
      var list = new List<T>();

      switch (this.TreeList.OptionsView.ShowCheckBoxes) {
        case true: // 复选模式
          foreach (TreeListNode node in this.TreeList.Nodes) {
            // 递归方法
            GetCheckedTreeListNodesFull<T>(node, list);
          }
          break;
        case false: // 单选模式
          list.Add((T)this.TreeList.GetDataRecordByNode(this.TreeList.FocusedNode));
          break;
      }

      return list;
    }

    /// <summary>
    /// 返回选中项列表（只返回叶子节点）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="node"></param>
    /// <param name="list"></param>
    private void GetCheckedTreeListNodes<T>(TreeListNode node, List<T> list) {
      // 如果是叶子节点，则返回结果
      if (node.Nodes.Count == 0) {
        if (node.Checked) {
          list.Add((T)this.TreeList.GetDataRecordByNode(node));
        }

        return;
      }

      // 遍历子节点
      for (var i = 0; i < node.Nodes.Count; i++) {
        GetCheckedTreeListNodes<T>(node.Nodes[i], list);
      }
    }

    /// <summary>
    /// 返回选中项列表（返回所有节点）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="node"></param>
    /// <param name="list"></param>
    private void GetCheckedTreeListNodesFull<T>(TreeListNode node, List<T> list) {
      // 如果是叶子节点，则返回结果
      if (node.Nodes.Count == 0) {
        if (node.Checked) {
          list.Add((T)this.TreeList.GetDataRecordByNode(node));
        }

        return;
      }

      // 非叶子节点也添加到列表中
      if (node.Checked) {
        list.Add((T)this.TreeList.GetDataRecordByNode(node));
      }

      // 遍历子节点
      for (var i = 0; i < node.Nodes.Count; i++) {
        GetCheckedTreeListNodesFull<T>(node.Nodes[i], list);
      }
    }

    /// <summary>
    /// 返回选中项列表（包括未确定的状态）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public IEnumerable<T> GetCheckedTreeListNodesIndeterminate<T>() {
      var list = new List<T>();

      switch (this.TreeList.OptionsView.ShowCheckBoxes) {
        case true: // 复选模式
          foreach (TreeListNode node in this.TreeList.Nodes) {
            // 递归方法
            GetCheckedTreeListNodesIndeterminate<T>(node, list);
          }
          break;
        case false: // 单选模式
          list.Add((T)this.TreeList.GetDataRecordByNode(this.TreeList.FocusedNode));
          break;
      }

      return list;
    }

    /// <summary>
    /// 返回选中项列表（包括未确定的状态）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="node"></param>
    /// <param name="list"></param>
    private void GetCheckedTreeListNodesIndeterminate<T>(TreeListNode node, List<T> list) {
      // 如果是叶子节点，则返回结果
      if (node.Nodes.Count == 0) {
        if (node.Checked) {
          list.Add((T)this.TreeList.GetDataRecordByNode(node));
        }

        return;
      }

      // 非叶子节点也添加到列表中
      if (node.CheckState == CheckState.Checked || node.CheckState == CheckState.Indeterminate) {
        list.Add((T)this.TreeList.GetDataRecordByNode(node));
      }

      // 遍历子节点
      for (var i = 0; i < node.Nodes.Count; i++) {
        GetCheckedTreeListNodesIndeterminate<T>(node.Nodes[i], list);
      }
    }

    /// <summary>
    /// 返回待显示的字符串提示
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public string GetToolTip<T>() {
      var list = new List<string>();

      switch (this.TreeList.OptionsView.ShowCheckBoxes) {
        case true: // 复选模式
          var source = (List<T>)this.DataSource;

          // 遍历所有父节点
          foreach (TreeListNode node in this.TreeList.Nodes) {
            // 递归方法
            GetToolTip(source, node, list);
          }
          break;
        case false: // 单选模式
          list.Add((string)this.TreeList.FocusedNode[this.DisplayMember]);
          break;
      }

      return string.Join(",", list);
    }

    /// <summary>
    /// 返回待显示的字符串列表
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="source"></param>
    /// <param name="node"></param>
    /// <param name="list"></param>
    private void GetToolTip<T>(List<T> source, TreeListNode node, List<string> list) {
      // 如果是叶子节点，则返回
      if (node.Nodes.Count == 0) {
        if (node.Checked) {
          var model = source[node.Id];

          foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(typeof(T))) {
            if (!pd.Name.Equals(this.DisplayMember)) continue;

            list.Add((string)(pd.GetValue(model) ?? DBNull.Value));
          }
        }

        return;
      }

      // 遍历所有子节点
      for (int i = 0; i < node.Nodes.Count; i++) {
        GetToolTip(source, node.Nodes[i], list);
      }
    }

    /// <summary>
    /// 设置已勾选状态
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="K"></typeparam>
    /// <param name="data">待勾选的数据集合，K类型必须同fieldName对应的字段同类型</param>
    /// <param name="fieldName">匹配的字段</param>
    public void SetCheckedTreeListNodes<T, K>(IEnumerable<K> data, string fieldName) {
      if ((data?.Count() ?? 0) == 0) return;

      // 数据源
      var source = this.DataSource as List<T>;
      if (source == null) return;

      // 递归方法
      this.SetCheckedTreeListNodes<T, K>(source, this.TreeList.Nodes, data, fieldName);
    }

    /// <summary>
    /// 递归设置勾选状态
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="K"></typeparam>
    /// <param name="source">数据源</param>
    /// <param name="nodeList">节点集合</param>
    /// <param name="data">待勾选的数据集合</param>
    /// <param name="fieldName">匹配的字段，忽略大小写</param>
    private void SetCheckedTreeListNodes<T, K>(List<T> source, TreeListNodes nodeList, IEnumerable<K> data, string fieldName) {
      if (nodeList == null) return;

      foreach (TreeListNode node in nodeList) {
        // 先取消勾选状态
        node.Checked = false;

        var model = source[node.Id];

        foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(typeof(T))) {
          if (!pd.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase)) continue;

          // 判断传入的类型与指定字段的类型是否一致
          if (!typeof(K).Equals(pd.PropertyType))
            throw new Exception("指定的转换无效，请检查数据类型是否一致");

          // 如果匹配，则默认勾选
          if (data.Any(x => x.Equals((K)(pd.GetValue(model) ?? default(K))))) {
              node.Checked = true;
            SetCheckedParentNodes(node, node.CheckState);
          }
        }

        this.SetCheckedTreeListNodes(source, node.Nodes, data, fieldName);
      }
    }

    #endregion
    //private void My_FilterNode(object sender, DevExpress.XtraTreeList.FilterNodeEventArgs e)        {
    //  List<TreeListColumn> filteredColumns = e.Node.TreeList.Columns.Cast<TreeListColumn>().Where(c => c.FilterInfo.AutoFilterRowValue != null).ToList();
    //  if (filteredColumns.Count == 0) return;
    //  e.Handled = true;
    //  e.Node.Visible = filteredColumns.Any(c => IsNodeMatchFilter(e.Node, c));
    //}


    //internal void SetCriteriaOperator(string text) {
    //  DevExpress.Data.Filtering.CriteriaOperator cp = DevExpress.Data.Filtering.CriteriaOperator.Parse(text);
    //  this.TreeList.ActiveFilterCriteria = cp;
    //}
    static bool IsNodeMatchFilter(TreeListNode node, TreeListColumn column) {
      string filterValue = column.FilterInfo.AutoFilterRowValue.ToString();
      if (node.GetDisplayText(column).Contains(filterValue)) return true;

      foreach (TreeListNode n in node.Nodes) {
        if (IsNodeMatchFilter(n, column)) return true;
      }

      return false;
    }
  }
}
